﻿using Orleans;
using Orleans.Concurrency;
using Orleans.Runtime;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Threading.Tasks;

namespace iTool.ClusterComponent
{
    public interface IHashReader : iToolServiceWithStringKey
    {
        Task<ConcurrentDictionary<string, string>> GetAsync();
        Task<string> GetAsync(string field);
        Task SetAsync(string field, string payload);
        Task RemoveAsync(string field);
        Task RemoveAsync();
    }

    [Reentrant]
    public class HashReader : iToolServiceBase<HashState, HashExcuteBase>, IHashReader
    {
        public HashReader() : base(1000) { }
        public Task<ConcurrentDictionary<string, string>> GetAsync()
        {
            return Task.FromResult(this.State.keyValuePairs);
        }

        public Task<string> GetAsync(string field)
        {
            if (this.State.keyValuePairs != null)
            {
                this.State.keyValuePairs.TryGetValue(field, out string payload);
                return Task.FromResult(payload);
            }
                return Task.FromResult(string.Empty);

        }
        
        public Task SetAsync(string field, string payload)
        {
            this._SetAsync(field, payload);
            this.RaiseEvent(new HashSet { Field = field, Payload = payload });
            return Task.CompletedTask;
        }

        public void _SetAsync(string field, string payload)
        {
            this.State.keyValuePairs = this.State.keyValuePairs ?? new ConcurrentDictionary<string, string>();
            if (this.State.keyValuePairs.ContainsKey(field))
            {
                if (this.State.keyValuePairs[field] != payload)
                {
                    this.State.keyValuePairs[field] = payload;
                }
            }
            else
            {
                this.State.keyValuePairs.TryAdd(field, payload);
            }
        }

        public Task RemoveAsync(string field)
        {
            this._RemoveAsync(field);
            this.RaiseEvent(new HashRemoveField { Field = field});
            return Task.CompletedTask;
        }

        public  void _RemoveAsync(string field)
        {
            if (this.State.keyValuePairs != null)
            {
                this.State.keyValuePairs.Remove(field, out _);
            }
        }

        public Task RemoveAsync()
        {
            this.RaiseEvent(new HashRemove());
            return Task.CompletedTask;
        }

        public async override Task<bool> ApplyUpdatesToStorage(IReadOnlyList<HashExcuteBase> updates, int expectedversion)
        {
            var service = this.GrainFactory.GetGrain<IHashStorageService>(this.GetPrimaryKeyString());
            var serviceVersion = await service.GetVersionAsync();
            if (expectedversion < serviceVersion)
            {
                return false;
            }
            return true;
        }

        public async override Task<KeyValuePair<int, HashState>> ReadStateFromStorage()
        {
            var service = this.GrainFactory.GetGrain<IHashStorageService>(this.GetPrimaryKeyString());
            var state = await service.GetStateAsync();
            return new KeyValuePair<int, HashState>(state.Version, state);
        }

        protected async override void TransitionState(HashState state, HashExcuteBase @event)
        {
            var service = this.GrainFactory.GetGrain<IHashStorageService>(this.GetPrimaryKeyString());
            if (@event is HashSet set)
            {
                await service.SetAsync(set.Field, set.Payload);
            }
            else if (@event is HashRemoveField removeField)
            {
                await service.RemoveAsync(removeField.Field);
            }
            else
            {
                await service.RemoveAsync();
            }
        }

        protected override void NotifyStateChanged(IGrainFactory iGrainFactory)
        {
            //var service = iGrainFactory.GetGrain<IHashStorageService>(this.GetPrimaryKeyString());
            //var payload = await service.GetAsync();
            //this.State.keyValuePairs = payload;
        }
    }


    public class HashExcuteBase
    {

    }

    public class HashRemove : HashExcuteBase
    {

    }

    public class HashRemoveField : HashExcuteBase
    {
        public string Field { get; set; }
    }

    public class HashSet : HashExcuteBase
    {
        public string Field { get; set; }
        public string Payload { get; set; }
    }
}
